﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Lexical.Coding;
using System.Reflection;
using Kokomo.Lexical.Expressions;

namespace Kokomo.PeInspector.Reflection.Disassembly
{
	/// <summary>
	/// Describes the result of disassembly a method.
	/// </summary>
	/// <seealso cref="MsilDecompiler"/>
	public class MethodDecompilation : Decompilation
	{
		/// <summary>
		/// Gets the method disassembled.
		/// </summary>
		public MethodBase Method { get; private set; }
		/// <summary>
		/// Gets the MSIL instructions within the method body.
		/// </summary>
		public IEnumerable<Instruction> Instructions { get; private set; }

		internal IDictionary<Statement, MsilFrameInfo> FrameInfo { get; private set; }

		/// <summary>
		/// Gets a dictionary of <see cref="CodeMetricDefinition"/>s and their
		/// respective values.
		/// </summary>
		public IDictionary<CodeMetricDefinition, object> Metrics { get; private set; }
		/// <summary>
		/// Gets the constructor invoked from this method, if it is a constructor.
		/// </summary>
		public ConstructorInfo ChainedConstructor { get; private set; }
		/// <summary>
		/// Gets a list of arguments passed to <see cref="ChainedConstructor"/>.
		/// </summary>
		public IEnumerable<Expression> ChainedArguments { get; private set; }
		/// <summary>
		/// Gets a value indicating whether this constructor first
		/// initializes the object.
		/// </summary>
		/// <remarks>
		/// In C#, this is equivalent to invoking the default constructor
		/// in a value type.
		/// </remarks>
		public bool InitializesObject { get; private set; }

		internal MethodDecompilation(
			MethodBase method,
			IEnumerable<Instruction> instructions,
			IEnumerable<Statement> statements,
			IDictionary<Statement, MsilFrameInfo> frameInfoTable,
			ConstructorInfo chainedConstructor = null,
			IEnumerable<Expression> chainedArguments = null,
			bool initializesObject = false
			)
			:base(statements)
		{
			if (method == null) throw new ArgumentNullException("method");

			this.Method = method;
			this.Instructions = instructions;
			this.FrameInfo = frameInfoTable;

			this.Metrics = new Dictionary<CodeMetricDefinition, object>();

			this.ChainedConstructor = chainedConstructor;
			this.ChainedArguments = chainedArguments;
			this.InitializesObject = initializesObject;
		}

		public override string ToString()
		{
			return this.Method.ToString();
		}
	}
}
